package thread;


/**
 * 线程同步机制-互斥锁
 */
public class ThreadMutex {

    public static void main(String[] args) {

        /*
            1)在 java语言中，引入了对象互斥锁的概念，来保证共享数据操作的完整性
            2)每隔对象都对应一个可称为"互斥锁"的标记，这个标记用来保证在任一时刻，只能由一个线程访问该对象
            3)关键字 synchronized 来与对象的互斥锁联系。当某个对象用 synchronized 修饰时，表明该对象在任一时刻只能由一个线程访问
            4)同步的局限性：导致程序的执行效率要降低
            5)同步方法(非静态的)的锁可以是this，也可以是其他对象(要求是同一个对象)
            6)同步方法(静态的)的锁为当前类本身。类.class

            例：使用互斥锁synchronized互斥锁来解决售票问题
         */

        //要求多个线程的锁对象必须是同一个
        SellTicket03 s = new SellTicket03();
        new Thread(s).start();
        new Thread(s).start();
        new Thread(s).start();

        //如果使用 SellTicket03 继承 Thread来创建线程
        //那么 synchronized(this){}同步代码块 和同步方法就失效了
        //因为每次启动线程是 new SellTicket03().start(); 每次都会创建新对象，无法完成互斥
    }
}

//使用synchronized 实现线程同步
class SellTicket03 implements Runnable {

    private static int num = 100;   //让多个线程共享 num

    private boolean loop = true;


    //同步方法(静态的)的锁为当前类本身
    //public synchronized static void m1() {} 锁是加在 SellTicket03.class 上
    //如果在静态方法中，实现一个同步代码块 synchronized(类名.class){}
    public synchronized static void m1() {

    }

    public static void m2() {

//        synchronized (this) {
//            System.out.println("m2");
//        }
        synchronized (SellTicket03.class) {

        }
    }

    //1.public synchronized void sell() {} 就是一个同步方法
    //2.这时锁在 this对象
    //3.也可以在代码块上写 synchronized，同步代码块，互斥锁还是在 this 对象上
    public /*synchronized*/ void sell() { //同步方法，在同一个时刻，只能有一个线程来执行sell方法

        synchronized (this) {
            if (num <= 0) {
                System.out.println("售票结束...");
                loop = false;
                return;
            }
            System.out.println("窗口 " + Thread.currentThread().getName() + "售出1张票" + "剩余票数=" + (--num));
        }
    }

    @Override
    public void run() {
        while (loop) {
            sell(); //sell方法是一个同步方法
            //休眠50ms，模拟人休息
            try {
                Thread.sleep(50);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }
}
